<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>十字路口堵塞过程</title>
        <style type="text/css">
            canvas {
                width: 900px;
                height: 900px;
                border: 1px solid #000000;
            }

            .div-controls {
                margin-bottom: 12px;
            }

            button {
                width: 120px;
                height: 30px;
            }
        </style>
        
        <script type="text/javascript" src="./lib/SAT.js"></script>
        <script type="text/javascript" src="./lib/Vec2.js"></script>
        <script type="text/javascript" src="./lib/Polygon.js"></script>
        <script type="text/javascript">
            let W = 900;
            let H = 900;
            let centerR = 2.5; // 环岛中心小圆半径相对路宽的倍数
            let isRun = false;
            let ctx;
            let spirits;
            let road;
            let timerId;
            let allRoad = {};
            let allPaths;

            function forFloat(f) {
                return Math.round(f * 1000) / 1000;
            }

            function atan(x, y) {
                return Math.atan2(y, x);
            }

            function isZero(x) {
                x = Math.abs(x);
                return x < 1e-12;
            }

            function toDeg(rad) {
                let deg = rad / Math.PI * 180;
                deg = forFloat(deg);
                return deg;
            }

            function toRad(deg) {
                return deg / 180 * Math.PI;
            }
            class RoadSpan {
                constructor(type) {
                    // {type:'line', x1:0, y1:0, x2:100, y2:100}
                    // {type:'arc',x1:0, y1:0, x2:100, y2:100, x:0, y:0, r:100, a1:Math.PI*1.0, a2:Math.PI*1.5}
                    // 起点: (x1, y1)
                    // 终点: (x2, y2)
                    // 圆心: (x, y)
                    this.type = type;
                }
                swapP1P2() {
                    let x = this.x1;
                    this.x1 = this.x2;
                    this.x2 = x;
                    let y = this.y1;
                    this.y1 = this.y2;
                    this.y2 = y;
                }
            }
            class Road {
                constructor() {
                    this.myWidth = 30;
                    this.myHeight = 30;
                    this.lastCars = [];
                    this.cars = {};
                }
                createCar() {
                    if (!this.carCount) {
                        this.carCount = 1;
                    }
                    for (let i = 0; i < 4; i++) {
                        let car = this.lastCars[i];
                        let d = road.myWidth * 0.2;
                        if ((car == null ||
                                i == 0 && car.x > car.myWidth + d ||
                                i == 1 && car.y < H - car.myWidth - d ||
                                i == 2 && car.x < W - car.myWidth - d ||
                                i == 3 && car.y > car.myWidth + d) &&
                            Math.round(Math.random() * 10000) % 30 == 0) {
                            let deg = i * 90;
                            car = new Car(0);
                            car.id = this.carCount++;
                            this.cars[car.id] = car;
                            car.setPaths(allPaths['WSEN'.charAt(i)][Math.round(Math.random() * 100) % 3])
                            this.lastCars[i] = car;
                            spirits.push(car);
                        }
                    }
                }
                createCar_xxx() {
                    if (this.lastCars[0]) {
                        return;
                    }
                    let car;
                    let type = 3;
                    if (type == 1) {
                        car = new Car(Math.PI * 0.25);
                        // car.setPaths("S-C_L1,S-E_R,C-E_L1");
                        car.setPaths("E-C_L1,E-N_R,C-N_L1");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("S-C_L1,S-E_R,C-E_L1");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("W-C_L1,W-S_R,C-S_L1");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("N-C_L1,N-W_R,C-W_L1");
                        this.lastCars[0] = car;
                        spirits.push(car);
                    } else if (type == 2) {
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("E-C_L2,E-C_R,E-W_R,C-W_R,C-W_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("W-C_L2,W-C_R,W-E_R,C-E_R,C-E_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("N-C_L2,N-C_R,N-S_R,C-S_R,C-S_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("S-C_L2,S-C_R,S-N_R,C-N_R,C-N_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                    } else if (type == 3) {
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("E-C_L2,E-C_R,E-W_R,N-S_R,C-S_R,C-S_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("N-C_L2,N-C_R,N-S_R,W-E_R,C-E_R,C-E_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("W-C_L2,W-C_R,W-E_R,S-N_R,C-N_R,C-N_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                        //
                        car = new Car(Math.PI * 0.25);
                        car.setPaths("S-C_L2,S-C_R,S-N_R,E-W_R,C-W_R,C-W_L2");
                        this.lastCars[0] = car;
                        spirits.push(car);
                    }
                }
                render() {
                    this.createCar();
                    //
                    ctx.strokeStyle = "black";
                    ctx.fillStyle = "white";
                    ctx.lineWidth = 1;
                    ctx.setLineDash([]);
                    //
                    let x;
                    let y;
                    //
                    x = W / 2 - this.myWidth - 0.5;
                    y = H / 2 - this.myWidth - 0.5;
                    //
                    ctx.beginPath();
                    ctx.arc(W / 2, H / 2, this.myWidth * centerR, 0, Math.PI * 2, true);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.setLineDash([8]);
                    ctx.arc(W / 2, H / 2, this.myWidth * (centerR + 1), 0, Math.PI * 2, true);
                    ctx.stroke();
                    ctx.setLineDash([]);
                    //
                    let r2 = forR2(this.myWidth * (centerR + 2), this.myWidth);
                    let d = this.myWidth + r2;
                    x = W / 2;
                    y = H / 2;
                    //
                    ctx.beginPath();
                    ctx.arc(x + d, y + d, r2, Math.PI * 1.0, Math.PI * 1.5, false);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.arc(x - d, y + d, r2, Math.PI * 1.5, Math.PI * 2, false);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.arc(x - d, y - d, r2, Math.PI * 0, Math.PI * 0.5, false);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.arc(x + d, y - d, r2, Math.PI * 0.5, Math.PI * 1.0, false);
                    ctx.stroke();
                    // 右
                    ctx.moveTo(x + d, y + 0);
                    ctx.lineTo(W, y + 0);
                    ctx.moveTo(x + d, y + this.myWidth);
                    ctx.lineTo(W, y + this.myWidth);
                    ctx.moveTo(x + d, y - this.myWidth);
                    ctx.lineTo(W, y - this.myWidth);
                    // 左
                    ctx.moveTo(x - d, y + 0);
                    ctx.lineTo(0, y + 0);
                    ctx.moveTo(x - d, y + this.myWidth);
                    ctx.lineTo(0, y + this.myWidth);
                    ctx.moveTo(x - d, y - this.myWidth);
                    ctx.lineTo(0, y - this.myWidth);
                    // 下
                    ctx.moveTo(x + 0, y + d);
                    ctx.lineTo(x + 0, H);
                    ctx.moveTo(x + this.myWidth, y + d);
                    ctx.lineTo(x + this.myWidth, H);
                    ctx.moveTo(x - this.myWidth, y + d);
                    ctx.lineTo(x - this.myWidth, H);
                    // 上
                    ctx.moveTo(x + 0, y - d);
                    ctx.lineTo(x + 0, 0);
                    ctx.moveTo(x + this.myWidth, y - d);
                    ctx.lineTo(x + this.myWidth, 0);
                    ctx.moveTo(x - this.myWidth, y - d);
                    ctx.lineTo(x - this.myWidth, 0);
                    //
                    ctx.stroke();
                }
            }
            class Car {
                constructor(radian) {
                    this.myHeight = road.myWidth * 0.6; // 34;
                    this.myWidth = this.myHeight * 1.8;
                    this.x = 0;
                    this.y = 0;
                    this.radian = radian;
                    this.v = 4;
                }
                setPaths(names) {
                    let arr = names.split(",");
                    let span;
                    for (let i = 0; i < arr.length; i++) {
                        arr[i] = allRoad[arr[i]];
                    }
                    this.paths = arr;
                    this.pathIndex = -1;
                    //
                    this.toNextPath();
                }
                move(type) {
                    let radian = this.radian;
                    // console.log('radian=', radian)
                    if (type == 'back') {
                        this.x = this.x0;
                        this.y = this.y0;
                        this.a = this.a0;
                        this.radian = this.radian0;
                    } else {
                        this.x0 = this.x;
                        this.y0 = this.y;
                        this.a0 = this.a;
                        this.radian0 = this.radian;
                        //
                        radian = this.radian;
                        //
                        let span = this.paths[this.pathIndex];
                        if (span.type == 'line') {
                            this.x = this.x + this.v * Math.cos(radian);
                            this.y = this.y + this.v * Math.sin(radian);
                        } else if (span.type == 'arc') {
                            this.a += this.va;
                            if (this.va > 0 && this.a > span.a2 || this.va < 0 && this.a < span.a2) {
                                this.toNextPath();
                            } else {
                                // console.log('curA', forFloat(toDeg(curA)), 'endA', forFloat(toDeg(span.a2)))
                                this.x = span.x + span.r * Math.cos(this.a);
                                this.y = span.y + span.r * Math.sin(this.a);
                            }
                            this.forRadian();
                            radian = this.radian;
                        }
                        if (span.type == 'line') {
                            if (Math.abs(span.x1 - span.x2) + 1 < Math.abs(span.x1 - this.x) ||
                                Math.abs(span.y1 - span.y2) + 1 < Math.abs(span.y1 - this.y)) {
                                this.toNextPath();
                            }
                        }
                    }
                    //
                    let offset = this.myWidth * 2;
                    if (this.x + offset < 0 ||
                        this.x - offset > W ||
                        this.y + offset < 0 ||
                        this.y - offset > H) {
                        this.destroy = true;
                    }
                    //
                    this.compute4Point();
                    //
                    if (type != 'back' && !this.destroy && this.collide()) {
                        // console.log('collide');
                        this.move('back');
                    }
                }
                compute4Point() {
                    let points = this.compute4Point_base(this.x, this.y, this.radian,
                        this.myWidth, this.myHeight);
                    this.x1 = points[0][0];
                    this.y1 = points[0][1];
                    this.x2 = points[1][0];
                    this.y2 = points[1][1];
                    this.x3 = points[2][0];
                    this.y3 = points[2][1];
                    this.x4 = points[3][0];
                    this.y4 = points[3][1];
                }
                compute4Point_base(x, y, radian, myWidth, myHeight) {
                    let x1 = x + myHeight / 2 * Math.sin(radian);
                    let y1 = y - myHeight / 2 * Math.cos(radian);
                    let x2 = x1 - myHeight * Math.sin(radian);
                    let y2 = y1 + myHeight * Math.cos(radian);
                    let x3 = x2 - myWidth * Math.cos(radian);
                    let y3 = y2 - myWidth * Math.sin(radian);
                    let x4 = x3 + myHeight * Math.sin(radian);
                    let y4 = y3 - myHeight * Math.cos(radian);
                    return [
                        [x1, y1],
                        [x2, y2],
                        [x3, y3],
                        [x4, y4]
                    ];
                }
                forRadian() {
                    let span = this.paths[this.pathIndex];
                    if (span.type == 'line') {
                        let x = span.x2 - span.x1;
                        let y = span.y2 - span.y1;
                        this.radian = Math.atan2(y, x);
                    } else if (span.type == 'arc') {
                        if (this.va > 0) {
                            this.radian = Math.PI * 0.5 + this.a;
                        } else {
                            this.radian = -Math.PI * 0.5 + this.a;
                        }
                    }
                }
                toNextPath() {
                    if (this.pathIndex + 1 < this.paths.length) {
                        this.pathIndex++;
                    } else {
                        return;
                    }
                    let span = this.paths[this.pathIndex];
                    this.x = span.x1;
                    this.y = span.y1;
                    if (span.type == 'arc') {
                        this.a = span.a1;
                        // 每次前进角度
                        let d = span.r * 2 * Math.PI;
                        this.va = 2 * Math.PI * this.v / d;
                        if (span.a1 > span.a2) {
                            this.va = -this.va;
                        }
                    } else if (span.type == 'line') {}
                    this.forRadian();
                    this.compute4Point();
                    if (this.collide()) {
                        this.x = this.x0;
                        this.y = this.y0;
                        this.a = this.a0;
                        this.radian = this.radian0;
                        this.pathIndex--;
                        this.compute4Point();
                        console.log('换路线时碰撞');
                    }
                }
                getRange() {
                    // return {
                    // 	x: this.x3 - (this.x3 - this.x1) / 2,
                    // 	y: this.y3 - (this.y3 - this.y1) / 2,
                    // 	w: Math.abs(this.x3 - this.x1) + 2,
                    // 	h: Math.abs(this.y3 - this.y1) + 2
                    // };
                    return {
                        x: this.x3 - (this.x3 - this.x1) / 2,
                        y: this.y3 - (this.y3 - this.y1) / 2,
                        w: Math.max(this.x1, this.x2, this.x3, this.x4) - Math.min(this.x1, this.x2, this.x3, this
                            .x4),
                        h: Math.max(this.y1, this.y2, this.y3, this.y4) - Math.min(this.y1, this.y2, this.y3, this
                            .y4)
                    };
                }
                collide_xxx() {
                    for (let i = 0; i < spirits.length; i++) {
                        let item = spirits[i];
                        if (item instanceof Car && item != this) {
                            let r1 = this.getRange();
                            let r2 = item.getRange();
                            if (
                                Math.abs(r1.x - r2.x) < r1.w / 2 + r2.w / 2 &&
                                Math.abs(r1.y - r2.y) < r1.h / 2 + r2.h / 2
                            ) {
                                console.log('碰撞', this.id, item.id, forFloat(this.x1), forFloat(this.y1), forFloat(
                                    this.x4), forFloat(this.y4));
                                return true;
                            }
                        }
                    }
                    return false;
                }
                collide(isShow) {
                    let points1 = this.compute4Point_base(this.x, this.y, this.radian,
                        this.myWidth + 4, this.myHeight + 4);
                    for (let i = 0; i < spirits.length; i++) {
                        let item = spirits[i];
                        if (item instanceof Car && item != this) {
                            let points2 = item.compute4Point_base(item.x, item.y, item.radian,
                                item.myWidth + 4, item.myHeight + 4);
                            for (let j = 0; j < points2.length; j++) {
                                if (isInPolygon(points2[j], points1)) {
                                    if (isShow) {
                                        console.log(this.id, item.id);
                                    }
                                    return true;
                                }
                            }
                        }
                    }
                    return false;
                }
                render() {
                    ctx.strokeStyle = "steelblue"; //sandybrown
                    ctx.fillStyle = "steelblue";
                    ctx.lineWidth = 1;
                    //
                    //
                    this.move();
                    //
                    ctx.strokeStyle = "steelblue"; //sandybrown
                    ctx.fillStyle = "steelblue";
                    ctx.lineWidth = 1;
                    //
                    ctx.beginPath();
                    ctx.moveTo(this.x1, this.y1);
                    ctx.lineTo(this.x2, this.y2);
                    ctx.lineTo(this.x3, this.y3);
                    ctx.lineTo(this.x4, this.y4);
                    ctx.closePath();
                    ctx.fill();
                    ctx.fillStyle = "palevioletred";
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, true);
                    ctx.closePath();
                    ctx.fill();
                    //
                    ctx.font = "20px 宋体";
                    ctx.fillStyle = "black";
                    ctx.fillText(this.id, this.x + (this.x3 - this.x1) / 2, this.y);
                    // console.log('画车', this.x1, this.y1, this.myWidth, this.myHeight)
                    for (let i = 0; i < this.paths.length; i++) {
                        // drawRoadSpan(this.paths[i].name, 'red');
                    }
                }
            }

            function drawRoadSpan(name, color) {
                let span = allRoad[name];
                if (!span) {
                    return;
                }
                ctx.strokeStyle = color || "green";
                ctx.beginPath();
                if (span.r) {
                    if (span.reverse) {
                        ctx.arc(span.x, span.y, span.r, span.a2, span.a1, false);
                    } else {
                        ctx.arc(span.x, span.y, span.r, span.a1, span.a2, false);
                    }
                    ctx.stroke();
                }
                if (!span.r) {
                    ctx.moveTo(span.x1, span.y1);
                    ctx.lineTo(span.x2, span.y2);
                    ctx.stroke();
                }
            }

            function forAllRoad() {
                let r = road.myWidth * (centerR + 0.5);
                let d;
                let names = ["S-N_R", "E-W_R", "N-S_R", "W-E_R"];
                let span;
                for (let i = 0; i < 4; i++) {
                    span = new RoadSpan('arc');
                    span.r = r;
                    span.a2 = (1.75 - 0.5 * i) % 2 * Math.PI;
                    span.a1 = (0.25 - 0.5 * i) % 2 * Math.PI;
                    span.x = W / 2;
                    span.y = H / 2;
                    span.x1 = span.x + Math.cos(span.a1) * r;
                    span.y1 = span.y + Math.sin(span.a1) * r;
                    span.x2 = span.x + Math.cos(span.a2) * r;
                    span.y2 = span.y + Math.sin(span.a2) * r;
                    span.name = names[i];
                    span.reverse = true;
                    allRoad[span.name] = span;
                }
                //
                r = forR2(road.myWidth * (centerR + 0.5), road.myWidth * 0.5);
                d = road.myWidth * (centerR + 0.5) + r;
                names = ["E-C_R", "C-N_R", "S-C_R", "C-E_R", "W-C_R", "C-S_R", "N-C_R", "C-W_R"];
                let names2 = ["E-C_L2", "C-N_L2", "S-C_L2", "C-E_L2", "W-C_L2", "C-S_L2", "N-C_L2", "C-W_L2"];
                for (let i = 0; i < 8; i++) {
                    span = new RoadSpan('arc');
                    span.r = r;
                    span.a1 = (0.5 + 0.25 * i) % 2 * Math.PI;
                    span.a2 = (0.75 + 0.25 * i) % 2 * Math.PI;
                    let i2 = Math.floor(i / 2);
                    span.x = W / 2 + d * Math.cos((i2 * 0.5 + 1.75) * Math.PI);
                    span.y = H / 2 + d * Math.sin((i2 * 0.5 + 1.75) * Math.PI);
                    span.x1 = span.x + Math.cos(span.a1) * r;
                    span.y1 = span.y + Math.sin(span.a1) * r;
                    span.x2 = span.x + Math.cos(span.a2) * r;
                    span.y2 = span.y + Math.sin(span.a2) * r;
                    span.name = names[i];
                    allRoad[span.name] = span;

                    //
                    let spanLine = new RoadSpan('line');
                    if (i % 2 == 0) {
                        let a = Math.PI * (0 + 0.5 * Math.floor(i / 2));
                        spanLine.x1 = W / 2 + W / 2 * Math.cos(a);
                        spanLine.x1 += road.myWidth * 0.5 * Math.sin(a);
                        spanLine.y1 = H / 2 + H / 2 * Math.sin(a);
                        spanLine.y1 -= road.myWidth * 0.5 * Math.cos(a);
                        spanLine.x2 = span.x1;
                        spanLine.y2 = span.y1;
                    } else {
                        let a = Math.PI * (1.5 + 0.5 * Math.floor(i / 2));
                        spanLine.x1 = span.x2;
                        spanLine.y1 = span.y2;
                        spanLine.x2 = W / 2 + W / 2 * Math.cos(a);
                        spanLine.x2 -= road.myWidth * 0.5 * Math.sin(a);
                        spanLine.y2 = H / 2 + H / 2 * Math.sin(a);
                        spanLine.y2 += road.myWidth * 0.5 * Math.cos(a);
                    }
                    spanLine.name = names2[i];
                    allRoad[spanLine.name] = spanLine;
                }
                //
                r = forR2(road.myWidth * (centerR + 1.5), road.myWidth * 0.5);
                d = road.myWidth * (centerR + 1.5) + r;
                names = ["S-E_R", "W-S_R", "N-W_R", "E-N_R"];
                names2 = ["S-C_L1", "C-E_L1", "W-C_L1", "C-S_L1", "N-C_L1", "C-W_L1", "E-C_L1", "C-N_L1"];
                for (let i = 0; i < 4; i++) {
                    span = new RoadSpan('arc');
                    span.r = r;
                    span.a1 = (1 + 0.5 * i) % 2 * Math.PI;
                    span.a2 = (1.5 + 0.5 * i) % 2 * Math.PI;
                    span.x = W / 2 + d * Math.cos((i * 0.5 + 0.25) * Math.PI);
                    span.y = H / 2 + d * Math.sin((i * 0.5 + 0.25) * Math.PI);
                    span.x1 = span.x + Math.cos(span.a1) * r;
                    span.y1 = span.y + Math.sin(span.a1) * r;
                    span.x2 = span.x + Math.cos(span.a2) * r;
                    span.y2 = span.y + Math.sin(span.a2) * r;
                    span.name = names[i];
                    allRoad[span.name] = span;
                    //
                    let spanLine = new RoadSpan('line');
                    let a = Math.PI * (0.5 + 0.5 * i);
                    spanLine.x1 = W / 2 + W / 2 * Math.cos(a);
                    spanLine.x1 += road.myWidth * 0.5 * Math.sin(a);
                    spanLine.y1 = H / 2 + H / 2 * Math.sin(a);
                    spanLine.y1 -= road.myWidth * 0.5 * Math.cos(a);
                    spanLine.x2 = span.x1;
                    spanLine.y2 = span.y1;
                    spanLine.name = names2[i * 2];
                    allRoad[spanLine.name] = spanLine;
                    spanLine = new RoadSpan('line');
                    a = Math.PI * (0 + 0.5 * i);
                    spanLine.x1 = span.x2;
                    spanLine.y1 = span.y2;
                    spanLine.x2 = W / 2 + W / 2 * Math.cos(a);
                    spanLine.x2 -= road.myWidth * 0.5 * Math.sin(a);
                    spanLine.y2 = H / 2 + H / 2 * Math.sin(a);
                    spanLine.y2 += road.myWidth * 0.5 * Math.cos(a);
                    spanLine.name = names2[i * 2 + 1];
                    allRoad[spanLine.name] = spanLine;
                }
                //
                for (let name in allRoad) {
                    span = allRoad[name];
                    if (span.type == 'arc') {
                        for2PI(span);
                    }
                }
                span = allRoad["S-N_R"];
                span.a2 -= Math.PI * 2;
            }

            function for2PI(span) {
                if (span.a1 > 0 && span.a2 <= 0) {
                    span.a2 += 2 * Math.PI;
                } else if (span.a1 < 0) {
                    span.a1 += 2 * Math.PI;
                }
            }

            function initial() {
                spirits = [];
                road = new Road();
                spirits.push(road);
                let polygon=new Polygon(getPolygonVertices(4, 112));
                polygon.x=250;
                polygon.y=250;
                spirits.push(polygon);
                //
                forAllRoad();
                //
                allPaths = {};
                allPaths.E = [
                    "E-C_L1,E-N_R,C-N_L1",
                    "E-C_L2,E-C_R,E-W_R,C-W_R,C-W_L2",
                    "E-C_L2,E-C_R,E-W_R,N-S_R,C-S_R,C-S_L2"
                ];
                allPaths.S = [
                    "S-C_L1,S-E_R,C-E_L1",
                    "S-C_L2,S-C_R,S-N_R,C-N_R,C-N_L2",
                    "S-C_L2,S-C_R,S-N_R,E-W_R,C-W_R,C-W_L2"
                ];
                allPaths.W = [
                    "W-C_L1,W-S_R,C-S_L1",
                    "W-C_L2,W-C_R,W-E_R,C-E_R,C-E_L2",
                    "W-C_L2,W-C_R,W-E_R,S-N_R,C-N_R,C-N_L2"
                ];
                allPaths.N = [
                    "N-C_L1,N-W_R,C-W_L1",
                    "N-C_L2,N-C_R,N-S_R,C-S_R,C-S_L2",
                    "N-C_L2,N-C_R,N-S_R,W-E_R,C-E_R,C-E_L2"
                ];
            }

            function render() {
                if (!isRun) {
                    return;
                }
                ctx.clearRect(0, 0, W, H);
                for (let i = 0; i < spirits.length; i++) {
                    let item = spirits[i];
                    item.render(ctx);
                    if (item.destroy) {
                        spirits.splice(i, 1);
                        i--;
                    }
                }
                if (spirits.oldLength && spirits.oldLength > 1 && spirits.length <= 1) {
                    console.log('没车了');
                    clearInterval(timerId);
                    timerId = null;
                    isRun = false;
                }
                spirits.oldLength = spirits.length;
                //
                for (var name in allRoad) {
                    // drawRoadSpan(name, 'red');
                }
            }
            window.onload = function() {
                var c = document.getElementById("myCanvas");
                ctx = c.getContext("2d");
            }

            function btnStart_click() {
                isRun = !isRun;
                if (timerId) {
                    return;
                }
                initial();
                render();
                timerId = setInterval(render, 40);
            }

            function forR2(r1, d) {
                let a = 1;
                let b = 4 * d - 2 * r1;
                let c = 2 * Math.pow(d, 2) - Math.pow(r1, 2);
                let x = -b + Math.sqrt(Math.pow(b, 2) - 4 * a * c);
                x = x / (2 * a);
                return x;
            }

            // 判断点在多边形内
            function isInPolygon(checkPoint, polygonPoints) {
                var counter = 0;
                var i;
                var xinters;
                var p1, p2;
                var pointCount = polygonPoints.length;
                p1 = polygonPoints[0];
                for (i = 1; i <= pointCount; i++) {
                    p2 = polygonPoints[i % pointCount];
                    if (
                        checkPoint[0] > Math.min(p1[0], p2[0]) &&
                        checkPoint[0] <= Math.max(p1[0], p2[0])
                    ) {
                        if (checkPoint[1] <= Math.max(p1[1], p2[1])) {
                            if (p1[0] != p2[0]) {
                                xinters =
                                    (checkPoint[0] - p1[0]) *
                                    (p2[1] - p1[1]) /
                                    (p2[0] - p1[0]) +
                                    p1[1];
                                if (p1[1] == p2[1] || checkPoint[1] <= xinters) {
                                    counter++;
                                }
                            }
                        }
                    }
                    p1 = p2;
                }
                if (counter % 2 == 0) {
                    return false;
                } else {
                    return true;
                }
            }
            function polygonsCollisionTest (a, b) {
                var sides = a.getSides().concat(b.getSides()), axises = new Array();

                for (var j = 0, l = sides.length; j < l; j++) {
                    axises.push(sides[j].normL());
                }

                for (var i = 0, len = axises.length; i < len; i++) {
                    var axis = axises[i];

                    var proA = a.getProjection(axis),
                        proB = b.getProjection(axis);

                    if (isOverlay(proA, proB)) {
                        return false;
                    }
                }

                return true;
            }            
        </script>
    </head>
    <body>
        <div class="div-controls">
            <button class='btnStart' onclick="btnStart_click();">开始</button>
        </div>
        <canvas id="myCanvas" width="900" height="900"></canvas>
    </body>
</html>
